import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import dictionary.Avp;
import dictionary.RadiusDict;


public class RadiusAttribute {
	
	private byte[] value;
	
	private int type;
	
	private int length;
	
	Config cfg = Config.getInstance();
	
	public RadiusAttribute(String name, String value) {
		
		
		RadiusDict dict;
		
		try {
			dict = RadiusDict.getInstance(cfg.getRadiusDictionaryPath());
			//get the attribute from dictionary based on name
			Avp avp = dict.getAvpByName(name);
			
			//if vendor-specific, perform vendor specific handling
			if(avp.isVendorSpecific()) {
				//TODO: do the vendor specific parsing
			} else { //else get the type and value
				type = avp.getAvpCode();
				if (type ==dict.getAvpCode("User-Password")) {
					byte[] auth = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
					this.value = RadiusUtil.encryptPassword(auth, value.getBytes("ISO-8859-1"), "secret".getBytes());
				} else {
				    this.value = encodeAvpValue(avp, value); //encode value based on avp type
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public RadiusAttribute(int code, byte[] value, int length) {

		this.type = code;
		this.value = value;
		this.length = length;
	}
		
	
	private String decodeAvpValue(Avp avp, byte[] value) {
		// TODO Auto-generated method stub
		return null;
	}

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}
	
	public byte[] getValue() {
		return this.value;
	}

	public String getValueAsString() throws IOException {
		RadiusDict dict;
		dict = RadiusDict.getInstance(cfg.getRadiusDictionaryPath());
		Avp avp = dict.getAvpByType(this.type);
		switch (avp.getAvpValueFormat()) {
		case "string":
			return new String(value, "ISO-8859-1");
		case "text":
			return new String(value, "ISO-8859-1");
		case "integer":
			return fromByteArray(value)+"";
			//break;
		case "ipaddr":
		    try {
				return InetAddress.getByAddress(value).getHostAddress();
			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			break;
			
		case "time":
			return parseLong(value)+"";
			//break;
			
		default:
			break;
		}
		return null;
	}
	
	public void setValue(byte[] value) {
		this.value = value;
	}

	public int getCode() {
		return this.type;
	}
	
	public void setCode(int code) {
		this.type = code;
	}
	
	public int getLength() {
		//length = type-field(one octet) + length-field(one-octet) + length of value array
		return 2 + value.length;
	}
	
	private byte[] encodeAvpValue(Avp avp, String value) {
		switch (avp.getAvpValueFormat()) {
		case "string":
			try {
				return this.value = value.getBytes("ISO-8859-1");
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			break;

		case "text":
			try {
				return this.value = value.getBytes("UTF-8");
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			break;
			
		case "integer":
			return this.value = encodeUIntValue(value);
			//break;
			
		case "ipaddr":
		    try {
				return this.value = InetAddress.getByName(value).getAddress();
			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			break;
			
		case "time":
			return this.value = encodeUIntValue(value);
			//break;
			
		default:
			break;
		}
		
		return null;
	}
	
	
	
	private byte[] encodeUIntValue(String value) {
		long l = Long.parseLong(value);
		byte[] data = new byte[4];
		data[0] = (byte) (l >> 24);
		data[1] = (byte) (l >> 16);
		data[2] = (byte) (l >> 8);
		data[3] = (byte) (l);
		return data;
	}
	
	private int parseLong(byte[] data) {
		int l = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);
		l &= 0x0ffffffff;
		return l;
	}

	byte[] toByteArray(int value) {
	    return new byte[] {
	        (byte) (value >> 24),
	        (byte) (value >> 16),
	        (byte) (value >> 8),
	        (byte) value};
	}

	private int fromByteArray(byte[] bytes) {
	     return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
	}
}
